package se.liu.oschi129.world.objects;

import se.liu.oschi129.animation.Animation;
import se.liu.oschi129.world.model.World;
import se.liu.oschi129.world.model.WorldPhysicalRules;

/**
 * This subclass contains WorldObjects in motion (updated when performStepAction is called)
 * 
 * @author oschi129
 */
public abstract class WorldMovableObject extends WorldObject {
	
	// The horizontal and vertical speed
	private double horizontalSpeed = 0, verticalSpeed = 0;
	
	// The horizontal and vertical acceleration
	private double horizontalAcc = 0, verticalAcc = 0;
	
	// The physical rules for the object
	private WorldPhysicalRules physicalRules;
	
	/**
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 * - - - - - - - - - - - - Constructors - - - - - - - - - - - 
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 */
	
	public WorldMovableObject(Type type, Animation animation, boolean visible, double x, double y, double width, double height) {
		super(type, animation, visible, x, y, width, height);
	}
	
	public WorldMovableObject(Type type, double x, double y, double width, double height) {
		super(type, x, y, width, height);
	}
	
	public WorldMovableObject(double x, double y, double width, double height) {
		super(x, y, width, height);
	}
	
	/**
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 * - - - - - - - - - - Public methods - - - - - - - - - - - 
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 */
	
	/** Returns true if the object is movable, otherwise false */
    @Override public boolean isMovable() {
    	return true;
    }
    
    /** Sets the physical rules for the object */
    public void setPhysicalRules(WorldPhysicalRules physicalRules) {
    	this.physicalRules = physicalRules;
    }
    
    /** Returns the physical rules for the object */
    public WorldPhysicalRules getPhysicalRules() {
    	return physicalRules;
    }
    
	/** Sets the horizontal speed */
	public void setHorizontalSpeed(double speed) {
		horizontalSpeed = speed;
	}
	
	/** Returns the horizontal speed */
	public double getHorizontalSpeed() {
		return horizontalSpeed;
	}
	
	/** Sets the vertical speed */
	public void setVerticalSpeed(double speed) {
		verticalSpeed = speed;
	}
	
	/** Returns the vertical speed */
	public double getVerticalSpeed() {
		return verticalSpeed;
	}
	
	/** Sets the horizontal acceleration */
	public void setHorizontalAcceleration(double acceleration) {
		horizontalAcc = acceleration;
	}
	
	/** Returns the horizontal acceleration */
	public double getHorizontalAcceleration() {
		return horizontalAcc;
	}
	
	/** Sets the vertical acceleration */
	public void setVerticalAcceleration(double acceleration) {
		verticalAcc = acceleration;
	}
	
	/** Returns the vertical acceleration */
	public double getVerticalAcceleration() {
		return verticalAcc;
	}
	
	/** Moves the object to the spcified cordinates with a set speed. Must be used within a TimerTask*/
	public void moveToPosition (double x, double y, double speed){
		double direction = Math.atan2(y - getY(), x - getX());
		setHorizontalSpeed(Math.cos(direction)*speed);
		setVerticalSpeed(Math.sin(direction)*speed);
	}
	
	/** Performs a step action for the object */
	@Override public void performStepAction(World world) {
		super.performStepAction(world);
		setX(getX() + getHorizontalSpeed());
		setY(getY() + getVerticalSpeed());
		setHorizontalSpeed(getHorizontalSpeed() + getHorizontalAcceleration());
		setVerticalSpeed(getVerticalSpeed() + getVerticalAcceleration());
	}
	
	 /**
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 * - - - - - - - - - - Protected methods - - - - - - - -
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 */
	
	/** Makes the object fall towards the ground. This method is protected -  
	 *  it may only be used within the performStepAction. */
	protected void realizeGravity(World world) {
		if (world.placeFree(this, getX(), getY() + getVerticalSpeed())) {
			// No object below - accelerate towards the ground
			setVerticalAcceleration(getPhysicalRules().getGravity());
		} else {
			// There is an object below - stop the vertical motion
			setVerticalSpeed(0);
			setVerticalAcceleration(0);
			if (getVerticalSpeed() > 0) {
				world.makeContact(this, 0, 1); // Move until a contact is made
			} else if (getVerticalSpeed() < 0) {
				world.makeContact(this, 0, -1); // Move until a contact is made
			}
		}
		// Set the maximal falling speed
		if (getVerticalSpeed() > getPhysicalRules().getMaxFallSpeed()) {
			setVerticalSpeed(getPhysicalRules().getMaxFallSpeed());
			setVerticalAcceleration(0);
		}
	}

	/** Makes the object slow down. This method is protected -  
	 *  it may only be used within the performStepAction. */
	protected void realizeFriction(World world) {}
	
}
